import i18next from 'i18next';
import queryString from 'query-string';
import Aberrant from '@/components/Aberrant';

export interface Msg {
  batchId: string;
  msgType: string;
  // eslint-disable-next-line
  body: Record<string, any>;
  success: boolean;
  errorMsg?: string
}

interface SendMsg {
  msgType?: string;
  // eslint-disable-next-line
  body?: Record<string, any>;
}

type ListenKey = (msg: Msg) => void;

interface Options {
  onConnect: () => void;
  onMessage?: (msg: Msg) => void;
}

class Socket {
  socket: null | WebSocket;

  timer: NodeJS.Timeout | null = null;

  listenMap = new Map();

  constructor() {
    this.socket = null;
  }

  connect({ onConnect, onMessage }: Options) {
    if (this.socket) {
      return;
    }

    if (import.meta.env.DEV) {
      this.socket = new WebSocket(`wss:${import.meta.env.VITE_API_URL}/mine-s/mineServer/234?talentId=456&token=jpm4hmv2okcal0z4k1xe`);
    } else {
      const parsed = queryString.parse(location.search);
      this.socket = new WebSocket(`wss:${import.meta.env.VITE_API_URL}/mine-s/mineServer/${parsed.userId}?talentId=${parsed.talentId}&token=${parsed.token}`);
    }
    this.socket.binaryType = 'arraybuffer';

    this.socket.onopen = () => {
      this.timer = setInterval(() => {
        if (this.socket) {
          this.socket.send(JSON.stringify({ msgType: 'HB' }));
        }
      }, 10000);
      onConnect();
    };

    this.socket.onmessage = (event) => {
      const message = event.data instanceof ArrayBuffer ? event.data : JSON.parse(event.data); // 解析收到的消息
      if (typeof message.body === 'string' && message.body.startsWith('{')) {
        message.body = JSON.parse(message.body);
      }
      onMessage && onMessage(message);

      if (this.listenMap.size !== 0) {
        this.listenMap.forEach((value, fn) => {
          if (value.find((type: string) => type === '*')) {
            fn(message);
            return;
          }

          const msgType = value.find((type: string) => type === message.msgType);
          if (msgType) {
            fn(message);
          }
        })
      }

      if (message.msgType === 'CONNECT_RESULT' && !message.success) {
        Aberrant(message.errorMsg);
      }

      if ([
        'SYS_SYSTEM',
        'BIZ_SYSTEM',
        'INVALID_PARAMETER',
        'USER_NOT_EXIST',
        'TALENT_NOT_OPENED',
        'SIGN_ERROR',
        'DB_OPERATE_FAIL',
      ].includes(message.msgType)) {
        Aberrant(message.body.errorMsg);
      }
    };

    this.socket.onclose = () => {
      if (this.timer) {
        clearInterval(this.timer);
      }

      Aberrant(i18next.t('broken'), () => {
        window.location.reload();
      });
    }
  }

  send(msg: SendMsg, options?: { monitor: ListenKey, listenTypes: string[] }) {
    if (this.socket !== null) {
      const data = JSON.stringify(msg);
      this.socket.send(data);
    }

    if (options) {
      this.listenMap.set(options.monitor, options.listenTypes);
    }
  }

  disconnect() {
    if (this.socket !== null) {
      this.socket.close();
    }
  }

  listen(types: string[], key: ListenKey) {
    this.listenMap.set(key, types);
  }

  unListen(key: ListenKey) {
    this.listenMap.delete(key);
  }
}

export default new Socket();